﻿using BoYuanCore.Framework;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Data.Common;
using System.Linq;
using System.Text;
using BoYuanCore.DBServices.Base;
using BoYuanCore.Web;
using FreeSql;
using FreeSql.Aop;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;

namespace BoYuanCore.WebUI.Code.Extensions
{
    public static class FreeSqlSetup
    {
        //注入方式配置FreeSql。本框架暂时不用此注入
        /*
        public static void AddFreeSqlSetup(this IServiceCollection services)
        {
            if (services == null) throw new ArgumentNullException(nameof(services));
            
            services.AddSingleton<IFreeSql>(o =>
            {
                return new FreeSql.FreeSqlBuilder()
                    .UseConnectionString(GetDbType(), _ConnectionString)
                    .UseAutoSyncStructure(true) //自动同步实体结构到数据库
                    .Build(); //请务必定义成 Singleton 单例模式
            });
        }

        private static string _ConnectionString => Appsettings.app(new string[] { "AppSettings", "DbConnection", "ConnectionString" });
        private static FreeSql.DataType GetDbType()
        {
            string dbType = Appsettings.app(new string[] { "AppSettings", "DbConnection", "DbType" });
            switch (dbType.ToLower())
            {
                case "postgresql":
                case "pgsql":
                    return FreeSql.DataType.PostgreSQL;
                case "sqlite":
                    return FreeSql.DataType.Sqlite;
                case "oracle":
                    return FreeSql.DataType.Oracle;
                case "mysql":
                    return FreeSql.DataType.MySql;
                default://默认为mssql
                    return FreeSql.DataType.SqlServer;
            }
        }
        */

        #region FreeSql AOP
        public static void FreeSqlAop()
        {
            var fsql = FreeSqlHelper.GetInstance();

            //全局帮助实现“软删除”、“租户”等设计 ，参考 https://github.com/dotnetcore/FreeSql/wiki/%E8%BF%87%E6%BB%A4%E5%99%A8


            fsql.Aop.CurdAfter += (s, e) => 
            {
#if DEBUG
                //获取执行后的总毫秒数
                long sqlExecutionTotalMilliseconds = e.ElapsedMilliseconds;
#endif

                if (e.Exception != null)
                {
                    StringBuilder sb_SugarParameterStr = new StringBuilder("###DbParms参数为:");
                    var parametres = e.DbParms;
                    if (parametres != null)
                    {
                        sb_SugarParameterStr.Append(JsonConvert.SerializeObject(parametres));
                    }

                    StringBuilder sb_error = new StringBuilder();
                    sb_error.AppendLine("FreeSqlClient执行sql异常信息:" + e.Exception.Message);
                    sb_error.AppendLine("###赋值后sql:" + LookSQL(e.Sql, parametres));
                    sb_error.AppendLine("###带参数的sql:" + e.Sql);
                    sb_error.AppendLine("###参数信息:" + sb_SugarParameterStr.ToString());
                    sb_error.AppendLine("###StackTrace信息:" + e.Exception.StackTrace);

                    //记录日志，这里用serilog静态方法，暂不用ilogger
                    Serilog.Log.Error(System.Environment.NewLine + sb_error.ToString());
                }

            };

            fsql.Aop.CurdBefore += (s, e) =>
            {
#if DEBUG 
                //输出到控制台显示SQL
                string sql = LookSQL(e.Sql, e.DbParms);
                Serilog.Log.Information(System.Environment.NewLine
                                        + "-----------------------------------sql-----------------------------------"+ System.Environment.NewLine
                                        + sql
                                        + System.Environment.NewLine + "-----------------------------------sql-----------------------------------");
#endif

                //freesql会自动拦截以下功能，导致执行失败而不抛异常。以下代码先留着吧！！！
                //判断删除或修改方法是否含有where条件.如果真的想更新或删除所有数据，请where(p=>true)或where(p=>p.id>0)
                // if (e.CurdType == CurdType.Update || e.CurdType == CurdType.Delete)
                // {
                //     string funStr = e.CurdType.ToString();
                //     if (e.Sql.IndexOf("where", StringComparison.CurrentCultureIgnoreCase) <= 0)
                //     {
                //         throw new Exception(funStr + "方法需要有where条件！！");
                //     }
                // }
            };

            //参考：https://github.com/dotnetcore/FreeSql/wiki/AOP

            //fsql.Aop.ParseExpression += (s, e) => {
            //    if (e.Expression.NodeType == Call && e.Expression.Name == "get_Item")
            //        e.Result = "1111";
            //};

            //fsql.Aop.AuditValue += (s, e) => {
            //    if (e.Column.CsType == typeof(long) //freesql自带的雪花特性
            //        && e.Property.GetCustomAttribute<SnowflakeAttribute>(false) != null
            //        && e.Value?.ToString() == 0)
            //        e.Value = new Snowflake().GetId();
            //};


            //fsql.Aop.AuditDataReader += (obj, ae) =>
            //{
            //    //处理oracle特有的空字符串存储为null的问题，查询时候string默认null转为空字符串
            //    //https://github.com/dotnetcore/FreeSql/issues/436
            //    if (fsql.Ado.DataType == DataType.Oracle)
            //    {
            //        if (ae.DataReader.GetFieldType(ae.Index) == typeof(string) && ae.Value == DBNull.Value)
            //            ae.Value = string.Empty;
            //    }
            //};


            fsql.Aop.ConfigEntityProperty += (s, ce) =>
            {
                //默认情况 c# 枚举会映射为 MySql Enum 类型，如果想映射为 int 在 FreeSqlBuilder Build 之后执行以下 Aop 统一处理。https://github.com/dotnetcore/FreeSql/wiki/%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98
                // if (fsql.Ado.DataType == DataType.MySql && ce.Property.PropertyType.IsEnum)
                //     ce.ModifyResult.MapType = typeof(int);

                //为了LookSQL能正常显示Enum特性的值
                if (ce.Property.PropertyType.IsEnum)
                    ce.ModifyResult.MapType = typeof(int);
            };
        }

        /// <summary>
        /// 查看赋值后的sql
        /// </summary>
        /// <param name="sql"></param>
        /// <param name="pars">参数</param>
        /// <returns></returns>
        private static string LookSQL(string sql, DbParameter[] pars)
        {
            if (pars == null || pars.Length == 0) return sql;

            StringBuilder sb_sql = new StringBuilder(sql);
            var tempOrderPars = pars.Where(p =>  p != null && p.Value != null).OrderByDescending(p => p.ParameterName.Length).ToList();//防止 @par1错误替换@par12
            for (var index = 0; index < tempOrderPars.Count; index++)
            {
                sb_sql.Replace(tempOrderPars[index].ParameterName, "'" + tempOrderPars[index].Value.ToString() + "'");
            }

            return sb_sql.ToString();
        }
        #endregion
    }
}
